package org.shaka.cli;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.shaka.tools.StringTools;

import com.google.common.base.Optional;

public class StringFilterComponent {
    public static abstract class StringFilter {
        private static final EmptyStringFilter empty = new EmptyStringFilter();
        private StringFilter next;

        public StringFilter() {
            this(null);
        }

        public StringFilter(StringFilter next) {
            this.next = next;
        }

        public String filter(String source) throws Exception {
            return Optional.fromNullable(next).or(empty).doFilter((doFilter(source)));
        }

        public abstract String doFilter(String source) throws Exception;
    }

    public static class EmptyStringFilter extends StringFilter {
        @Override
        public String doFilter(String source) throws Exception {
            return source;
        }
    }

    public static class RegularReplaceFilter extends StringFilter {
        private Pattern elPattern = Pattern.compile("(\\$\\{.*?\\})");
        private Pattern anylisysElPattern = Pattern.compile("\\$\\{(\\w+)\\(([^\\)]+)\\)\\}");
        private String regex;
        private String replacement;

        public RegularReplaceFilter(String regex, String replacement) {
            this.regex = regex;
            this.replacement = replacement;
        }

        @Override
        public String doFilter(String source) throws Exception {
            Matcher replacementM = elPattern.matcher(replacement);
            if (!replacementM.find()) {
                return Pattern.compile(regex).matcher(source).replaceAll(replacement);
            }

            StringBuffer sb = new StringBuffer();
            Matcher sourceMatcher = Pattern.compile(regex).matcher(source);
            while (sourceMatcher.find()) {
                String replaceStr = replacement;
                replacementM = elPattern.matcher(replaceStr);
                while (replacementM.find()) {
                    String elExpressStr = replacementM.group();
                    Matcher elAnylisysM = anylisysElPattern.matcher(elExpressStr);
                    elAnylisysM.find();

                    String methodStr = elAnylisysM.group(1);
                    String[] splitA = elAnylisysM.group(2).split(",");
                    Object[] inputArgs = new Object[splitA.length];
                    System.arraycopy(splitA, 0, inputArgs, 0, splitA.length);
                    for (int i = 0; i < inputArgs.length; i++) {
                        String s = (String) inputArgs[i];
                        if (s.matches("^\\$\\d+$")) {
                            inputArgs[i] = sourceMatcher.group(Integer.parseInt(s.replaceAll(".(\\d+)", "$1")));
                        }
                    }
                    for (Method method : StringTools.class.getMethods()) {
                        if (method.getName().equals(methodStr) && method.getParameterTypes().length == inputArgs.length) {
                            for (int i = 0; i < method.getParameterTypes().length; i++) {
                                Class<?> c = method.getParameterTypes()[i];
                                if (c == String.class)
                                    continue;
                                if (c == Boolean.class || c == boolean.class) {
                                    inputArgs[i] = ((String) inputArgs[i]).matches("^(1|true|TRUE|y|Y|yes|YES|ok|OK)$");
                                } else if (c == Short.class || c == short.class) {
                                    inputArgs[i] = Short.parseShort((String) inputArgs[i]);
                                } else if (c == Integer.class || c == int.class) {
                                    inputArgs[i] = Integer.parseInt((String) inputArgs[i]);
                                } else if (c == Long.class || c == long.class) {
                                    inputArgs[i] = Long.parseLong((String) inputArgs[i]);
                                } else if (c == Float.class || c == float.class) {
                                    inputArgs[i] = new BigDecimal((String) inputArgs[i]).floatValue();
                                } else if (c == Double.class || c == double.class) {
                                    inputArgs[i] = new BigDecimal((String) inputArgs[i]).doubleValue();
                                }
                            }
                            Object result = method.invoke(StringTools.class, inputArgs);
                            replaceStr = replaceStr.replace(elExpressStr, String.valueOf(result));
                            replacementM = elPattern.matcher(replaceStr);
                            break;
                        }
                    }
                }
                sourceMatcher.appendReplacement(sb, replaceStr);
            }
            return sourceMatcher.appendTail(sb).toString();
        }
    }
}
